﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;

namespace GameTank
{
    public class Character : VisibleGameEntity
    {
        //CharacterDTO _detail;
        //MySprite2D _model;
        //public Character(ContentManager Content)
        //{
        //    _model = new MySprite2D(8, 4, Content);
        //}


        CharacterController controller;

        public CharacterController Controller
        {
            get { return controller; }
            set { controller = value; }
        }


        CharacterDTO detail;

        public CharacterDTO Detail
        {
            get { return detail; }
            set { detail = value; }
        }
        static AudioLibrary audio;
        TimeCounter acttackDelay;
        List<Weapon> weapons;

        public List<Weapon> Weapons
        {
            get { return weapons; }
            set { weapons = value; }
        }

        //int nweapons;
        protected Texture2D[][] textures;
        protected int _nFrame = 8;
        protected static Texture2D[] explodingTextures;
        protected int _nExplodingFrame;
        protected SpriteBatch sBatch;
        
        // get screen bounder
        protected Rectangle battleBounds;
      
        public Character( CharacterDTO newDetail, ContentManager Content)
        {
            //_model = new MySprite2D(8, 4, Content);
            //DTO;
            
            acttackDelay = new TimeCounter();
            detail = newDetail;
            weapons = new List<Weapon>();
            acttackDelay.count(500.0f);
            if (detail.Controller == "HumanController")
            {
                this.controller = new HumanController(this);
            }
            else
            {
                this.controller = new AIController(this);
            }

            XDocument tempDoc = XDocument.Load(detail.LivingModel);
            ModelDTO livingMD = ModelDTO.Parse(tempDoc.Root);

            tempDoc = XDocument.Load(detail.ExplodingModel);
            ModelDTO explodingMD = ModelDTO.Parse(tempDoc.Root);



            for (int i = 0; i < detail.Weapons.Count; i++)
            {
                Weapon temp = new Weapon(this,detail.Weapons[i],Content);
                weapons.Add(temp);
                Childs.Add(temp);
            }

           
            // animation

            // init
            _nFrame = livingMD.NFrame;
            _nExplodingFrame = explodingMD.NFrame;
            audio = Game1.AudioLib;
            textures = new Texture2D[livingMD.NDirection][];
            for (int i = 0; i < livingMD.NDirection; i++)
            {
                textures[i] = new Texture2D[_nFrame];
                for (int j = 0; j < _nFrame; j++)
                {
                    string temp = livingMD.FilePath + i.ToString("0") + (j).ToString("00");
                    textures[i][j] = Content.Load<Texture2D>(temp);
                }
            }

            explodingTextures = new Texture2D[_nExplodingFrame];
            for (int i = 0; i < _nExplodingFrame; ++i)
            {
                string temp = explodingMD.FilePath + i.ToString("00");
                explodingTextures[i] = Content.Load<Texture2D>(temp);
            }
  
#if XBOX360
            // On the 360, we need to be careful about the TV's "safe" area.
            screenBounds = new Rectangle(
                    (int)(Game.Window.ClientBounds.Width * 0.03f),
                    (int)(Game.Window.ClientBounds.Height * 0.03f),
                    Game.Window.ClientBounds.Width - 
                    (int)(Game.Window.ClientBounds.Width * 0.03f),
                    Game.Window.ClientBounds.Height - 
                    (int)(Game.Window.ClientBounds.Height * 0.03f));
#else
            //screenBounds = new Rectangle(0, 0,
            //    Game.Window.ClientBounds.Width,
            //    Game.Window.ClientBounds.Height);

            battleBounds = Game1.battleBounder;
#endif
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            GamePlay gamePlay = Game1.gamePlay;
            if (detail.Ready)
            {
            }

            if (detail.Exploding)
            {
                if (this.Visible)
                {
                    if (detail.CurrentFrame < _nExplodingFrame*2)
                    {
                        detail.CurrentFrame++;
                    }
                    else
                    {
                        this.Visible = false;
                    }
                }
            }

            if (detail.Living)
            {
                Vector2 prePos = new Vector2(detail.Pos.X, detail.Pos.Y);
                int preHeight = detail.Height;
                int preWidth = detail.Width;

                detail.CurrentFrame = (int)(gameTime.TotalGameTime.TotalMilliseconds / detail.TimeDelay) % _nFrame;
                this.Controller.UpDatePosition(gameTime); // controller dieu khien charector
                
                
                //if (keyboard.IsKeyUp(Keys.Up) && keyboard.IsKeyUp(Keys.Down) && keyboard.IsKeyUp(Keys.Left) && keyboard.IsKeyUp(Keys.Right))
                //{
                //    detail.CurrentFrame = 0; // neu ca bon phim deu nha thi chon frame dau tien;
                //}

                detail.Height = textures[detail.Direction][detail.CurrentFrame].Height;
                detail.Width = textures[detail.Direction][detail.CurrentFrame].Width;
                
                // Keep the ship inside the screen  and out side construction
                {
                    Vector2 temp = detail.Pos;
                    //Vector2 prePos = new Vector2(detail.Pos.X,detail.Pos.Y);
                    if (detail.Pos.X < battleBounds.Left)
                    {
                        temp.X = battleBounds.Left;
                    }
                    if (detail.Pos.X > battleBounds.Width - detail.Width)
                    {
                        temp.X = battleBounds.Width - detail.Width;
                    }
                    if (detail.Pos.Y < battleBounds.Top)
                    {
                        temp.Y = battleBounds.Top;
                    }

                    if (detail.Pos.Y > battleBounds.Height - detail.Height)
                    {
                        temp.Y = battleBounds.Height - detail.Height;
                    }

                    detail.Pos = temp;
                    // kep the tank outside the construction

                    for (int ii = 0; ii < gamePlay.Childs.Count; ++ii)
                    {
                        if (gamePlay.Childs[ii] is Construction)
                        {
                            if (CheckCollision((Construction)(gamePlay.Childs[ii])))
                            {
                                Rectangle rect = ((Construction)(gamePlay.Childs[ii])).bounder;
                                Rectangle spriterect = new Rectangle((int)prePos.X, (int)prePos.Y,
                                    preWidth, preHeight);

                                if (spriterect.Right <= rect.Left)
                                {
                                    temp.X = rect.Left - detail.Width - 1;
                                }

                                if (spriterect.Left >= rect.Right)
                                {
                                    temp.X = rect.Right + 1;
                                }

                                if (spriterect.Top >= rect.Bottom)
                                {
                                    temp.Y = rect.Bottom + 1;
                                }

                                if (spriterect.Bottom <= rect.Top)
                                {
                                    temp.Y = rect.Top - detail.Height - 1;
                                }
                                detail.Pos = temp;
                            }
                        }// keep the tank outside construction

                        if (gamePlay.Childs[ii] is Character)
                        {
                            if (CheckCollision((Character)(gamePlay.Childs[ii])) // if having collision
                                && ((Character)(gamePlay.Childs[ii])).faction != this.detail.Faction  // if character is an enemy
                                && !((Character)(gamePlay.Childs[ii])).isDead()) // and enemt is not dead
                            {
                                float tempDamage = ((Character)(gamePlay.Childs[ii])).getCurrentHp;
                                ((Character)(gamePlay.Childs[ii])).beAttack(this.detail.CurrentHp);
                                Character thisTank = this;
                                thisTank.beAttack(tempDamage);
                                this.detail.CurrentExp += ((Character)(gamePlay.Childs[ii])).getExp;                               
                            }
                        }

                    }
                }// end keep the tank in right position;

                // check Collision with the orthers enemy character
                
                // fire 
                this.Controller.UpDateFire(gameTime);                

            }
            base.Update(gameTime);
        }

        public void MoveUp() // goi phuong thuc nay doi tuong se di chuyen len tren
        {
            detail.Direction = 0;
            detail.Pos += detail.Velocity;
        }
        public void MoveDown() // goi phuong thuc nay doi tuong di chuyen xuong
        {
            detail.Direction = 1;
            detail.Pos += detail.Velocity;
        }
        public void MoveLeft()
        {
            detail.Direction = 2;
            detail.Pos += detail.Velocity;
        }
        public void MoveRight()
        {
            detail.Direction = 3;
            detail.Pos += detail.Velocity;
        }
        public void stopMove() // khi tha bang phim amination di chuyen phai dung lai
        {
            detail.CurrentFrame = 0;
        }
       
        public void fire()  // phuong thuc nay duoc goi khi xe tang muon ban
        {
            if (acttackDelay.Ready)
            {
                Vector2 fireLoc = new Vector2(detail.Pos.X + (detail.Width / 2), detail.Pos.Y + (detail.Height / 2));
                switch (detail.Direction)
                {
                    case 0:
                        fireLoc.Y = detail.Pos.Y;
                        break;
                    case 1:
                        fireLoc.Y = detail.Pos.Y + detail.Height;
                        break;
                    case 2:
                        fireLoc.X = detail.Pos.X;
                        break;
                    case 3:
                        fireLoc.X = detail.Pos.X + detail.Width;
                        break;
                    default:
                        break;
                }
                for (int i = 0; i < detail.Weapons.Count; ++i)
                {
                    if (weapons[i].IsReady)
                    {
                        weapons[i].fire(fireLoc, detail.Direction);
                        break;
                    }
                }
                acttackDelay.recount();
            }
        }

        public void PutinStartPosition()
        {
            detail.Pos = new Vector2(battleBounds.Width / 2, battleBounds.Height - detail.Height);
        }

        public override void Draw(GameTime gameTime, SpriteBatch sBatch)
        {
            
            if (detail.Living)
            {
                sBatch.Begin();
                sBatch.Draw(textures[detail.Direction][detail.CurrentFrame], detail.Pos, Color.White);
                sBatch.End();
            }
            
            if (detail.Exploding)
            {
                if (detail.CurrentFrame <= _nExplodingFrame && this.Visible)
                {
                    sBatch.Begin();
                    sBatch.Draw(explodingTextures[detail.CurrentFrame-1], detail.Pos, Color.White); 
                    sBatch.End();
                }
            }
            base.Draw(gameTime, sBatch);
        }

        public bool CheckCollision(Construction orther)
        {
            Rectangle rect = orther.bounder;
            Rectangle spriterect = new Rectangle((int)detail.Pos.X, (int)detail.Pos.Y,
                       detail.Width, detail.Height);
            return spriterect.Intersects(rect);
        }

        public void acttackVictim(Character victim)
        {
            victim.beAttack(this.detail.Acttack);
            if (victim.isDead())
            {
                this.detail.CurrentExp += victim.getExp;
                //victim.removeFromTheScreen();
            }
        }
        //
        //float ICharacter.getDamage
        //{
        //    get { return this.detail.Acttack; }
        //}
        //
        public bool CheckCollision(Character orther)
        {
            Rectangle rect = orther.bounder;
            Rectangle spriterect = new Rectangle((int)detail.Pos.X, (int)detail.Pos.Y,
                       detail.Width, detail.Height);
            return spriterect.Intersects(rect);
        }

        public int getExp
        {
            get { return (int)(this.detail.Exp / 20); }
        }
        
        void beAttack(float damage)
        {
            this.detail.CurrentHp -= damage;
            if (isDead())
            {                
                removeFromTheScreen();
            }
        }
        //
        public bool isDead()
        {
            if (detail.CurrentHp <= 0)
                return true;
            return false;
        }

        public void removeFromTheScreen()
        {
            this.detail.Exploding = true;
            audio.Explosion.Play(0.5f, 0.0f, 0.0f);
            this.detail.CurrentFrame = 1;
        }
        
        public Vector2 position()
        {
            return this.detail.Pos;
        }

        int Direction
        {
            get { return this.detail.Direction; }
            set
            {
                this.detail.Direction = value;
            }
        }
       
        public int faction
        {
            get { return this.detail.Faction; }
            set { this.detail.Faction = value; }
        }
        //
        public Rectangle bounder
        {
            get { return new Rectangle((int)detail.Pos.X, (int)detail.Pos.Y, detail.Width, detail.Height); }
            set { }
        }
        //
        //
        public void acttackVictim(Construction victim)
        {
            victim.beAttack(this.detail.Acttack);
        }
        //
        //CharacterDTO ICharacter.save
        //{
        //    get { return detail; }
        //}
        //
        float getCurrentHp
        {
            get { return detail.CurrentHp; }
        }
        
    }
}
